package cc.rengu.igas.mcps.core.packswitch;

import cc.rengu.igas.mcps.common.constant.McpsParamConstant;
import cc.rengu.igas.mcps.common.constant.McpsTreeNodeConstant;
import cc.rengu.igas.mcps.common.enums.RespCodeEnum;
import cc.rengu.igas.share.common.constant.UserSessionConstant;
import cc.rengu.jradp.packetswitch.IncomingPackSwitchImpl;
import cc.rengu.jradp.packetswitch.OutgoingPackSwitchImpl;
import cc.rengu.oltp.service.common.constant.AppParamConstant;
import cc.rengu.oltp.service.common.constant.TreeNodeConstant;
import cc.rengu.oltp.service.common.dao.SrcTxnAuthCfgMapper;
import cc.rengu.oltp.service.common.dao.impl.SrcTxnAuthCfgMapperImpl;
import cc.rengu.oltp.service.common.entity.SrcTxnAuthCfg;
import cc.rengu.oltp.service.common.enums.OltpRpcdEnum;
import cc.rengu.oltp.service.model.BizException;
import cc.rengu.oltp.service.model.TxnInfo;
import cc.rengu.oltp.service.realize.TxnInfoService;
import cc.rengu.oltp.service.realize.impl.TxnInfoServiceImpl;
import cc.rengu.oltp.utility.util.MD5Util;
import cc.rengu.oltp.utility.util.RedisUtil;
import cc.rengu.oltp.utility.util.XmlTreeUtil;
import cc.rengu.utility.log.RgLog;
import cc.rengu.utility.log.RgLogger;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.parser.Feature;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Map;

/**
 * 扫码支付通讯层报文解包前后、组前包后实现类
 * Created by lzl on 2018/6/1.
 *
 * @author sccba
 * @version 1.0
 * date 2021/05/12
 */
public class ScanPayServerPackSwitch implements IncomingPackSwitchImpl, OutgoingPackSwitchImpl {

    @Override
    public byte[] beforeInComPackSwitch(byte[] bytes) throws Exception {
        XmlTreeUtil xmlTreeUtil = new XmlTreeUtil();
        String jsonStr = new String(bytes);
        /* 转换XSS攻击的字符 */
        jsonStr = jsonStr.replaceAll("<", "＜").replaceAll(">", "＞");
        String signBlockString = generateSignBlockString(jsonStr);
        xmlTreeUtil.setXmlTreeStringValue(McpsTreeNodeConstant.SIGNBLOCK, signBlockString);
        return jsonStr.getBytes();
    }

    @Override
    public int afterInComPackSwitch() throws Exception {
        XmlTreeUtil xmlTreeUtil = new XmlTreeUtil();
        xmlTreeUtil.setXmlTreeStringValue("_MESSAGEHEAD/http/Content-Type", "application/json;charset=utf-8");
        xmlTreeUtil.setXmlTreeStringValue("_MESSAGEHEAD/http/ArgList", "Content-Type");
        RgLogger rglog = RgLog.getLogger(this.getClass().getName());
        String sessionId = xmlTreeUtil.getXmlTreeStringValue(TreeNodeConstant.HEADER_SESSION);

        //获取交易配置信息
        TxnInfoService txnInfoService = new TxnInfoServiceImpl();
        TxnInfo txnInfo = txnInfoService.getCurrentTxnInfo();
        xmlTreeUtil.setXmlTreeObjectValue(TreeNodeConstant.TXN_INFO,txnInfo);

        //签名验签 开启校验
        if (AppParamConstant.YES.equals(txnInfo.getSrcTxnAuthCfg().getSignFlag())) {
            String signBlockString = xmlTreeUtil.getXmlTreeStringValue(McpsTreeNodeConstant.SIGNBLOCK);
            String signature = xmlTreeUtil.getXmlTreeStringValue(TreeNodeConstant.HEADER_SIGNATURE);

            /* 从Redis中获取工作密钥用于验签 */
            if (sessionId == null || "".equals(sessionId.trim())) {
                rglog.error("sessionId为空，用户超时");
                xmlTreeUtil.setXmlTreeStringValue(McpsTreeNodeConstant.IS_LOGIN, AppParamConstant.NO);
                return 0;
            }
            String signatureKey = RedisUtil.hashGet(sessionId, UserSessionConstant.SIGNATURE_KEY);
            if (signatureKey == null) {
                rglog.error("用户会话不存在!");
                // 不存在Session
                xmlTreeUtil.setXmlTreeStringValue(McpsTreeNodeConstant.IS_LOGIN, AppParamConstant.NO);
                return 0;
            }
            xmlTreeUtil.setXmlTreeStringValue(McpsTreeNodeConstant.WORK_KEY, signatureKey);
            if (!MD5Util.verifySignString(signature, signBlockString, McpsParamConstant.KEY_EQUALS, signatureKey)) {
                rglog.error("验签失败!sign:<{}>signBlock:<{}>", signature, signBlockString);
                xmlTreeUtil.setXmlTreeStringValue(McpsTreeNodeConstant.VERIFY_SIGN_FLAG, AppParamConstant.NO);
            }
        }

        return 0;
    }

    @Override
    public int beforeOutgoPackSwitch() throws Exception {
        return 0;
    }

    @Override
    public byte[] afterOutgoPackSwitch(byte[] bytes) throws Exception {
        RgLogger rglog = RgLog.getLogger(this.getClass().getName());
        XmlTreeUtil xmlTreeUtil = new XmlTreeUtil();
        TxnInfo txnInfo = (TxnInfo) xmlTreeUtil.getXmlTreeObjectValue(TreeNodeConstant.TXN_INFO, TxnInfo.class);
        if (null == txnInfo) {
            TxnInfoService txnInfoService = new TxnInfoServiceImpl();
            txnInfo = txnInfoService.getCurrentTxnInfo();
        }
        if (McpsParamConstant.HEALCHTK.equals(txnInfo.getSrcTxnAuthCfg().getTxnNum())) {
            return new byte[0];
        }
        if (AppParamConstant.NO.equals(txnInfo.getSrcTxnAuthCfg().getSignFlag())) {
            return bytes;
        }
        String jsonBefore = new String(bytes);
        String signBlock = generateSignBlockString(jsonBefore);
        /* 生成生成数字签名的字符串 */
        if (null == signBlock) {
            rglog.error("生成加签Block失败!");
            throw new BizException(RespCodeEnum.GENERATE_SIGN_ERROR.getRespCode(), RespCodeEnum.GENERATE_SIGN_ERROR.getRespDesc());
        }
        rglog.debug("加签Block:<{}>", signBlock);
        String md5Key = xmlTreeUtil.getXmlTreeStringValue(McpsTreeNodeConstant.WORK_KEY);
        String signData = MD5Util.generateSignString(signBlock, "&key=", md5Key);
        if (null == signData) {
            rglog.error("生成数字签名失败!");
            throw new BizException(RespCodeEnum.GENERATE_SIGN_ERROR.getRespCode(), RespCodeEnum.GENERATE_SIGN_ERROR.getRespDesc());
        }
        rglog.debug("数字签名:<{}>", signData);

        JSONObject jsonObj = JSON.parseObject(jsonBefore, Feature.OrderedField);
        JSONObject headObj = jsonObj.getJSONObject(McpsTreeNodeConstant.HEADER);
        headObj.put(McpsTreeNodeConstant.SIGN, signData);
        jsonObj.put(McpsTreeNodeConstant.HEADER, headObj);
        rglog.debug("添加signature后发送报文:<{}>", jsonObj.toJSONString());
        return jsonObj.toJSONString().getBytes();
    }

    /**
     * 生成加签Block
     *
     * @param jsonString 原始报文
     * @return 加签BLOCK
     */
    private String generateSignBlockString(String jsonString) {
        RgLogger rglog = RgLog.getLogger(this.getClass().getName());
        try {
            if (null != jsonString && 0 != jsonString.trim().length()) {
                rglog.debug("jsonString=[{}]", jsonString);
                JSONObject jsonObject = JSONObject.parseObject(jsonString, Feature.OrderedField);
                Map maps = jsonObject.getInnerMap();
                ArrayList<String> arrayList = new ArrayList<>();
                int i = 0;
                Object key;
                Object value;
                for (Object map : maps.entrySet()) {
                    key = ((Map.Entry) map).getKey();
                    value = ((Map.Entry) map).getValue();
                    if (!"header".equals(key) && null != value) {
                        arrayList.add(key + "=" + value + "&");
                        i++;
                    } else if ("header".equals(key) && null != value) {
                        JSONObject headObj = JSONObject.parseObject(value.toString(), Feature.OrderedField);
                        headObj.remove("signature");
                        arrayList.add(key + "=" + headObj + "&");
                        i++;
                    } else {
                        i++;
                    }
                }
                String[] strArray = arrayList.toArray(new String[0]);
                Arrays.sort(strArray);
                StringBuilder stringBuffer = new StringBuilder();
                for (i = 0; i < strArray.length; i++) {
                    stringBuffer.append(strArray[i]);
                }
                return stringBuffer.substring(0, stringBuffer.length() - 1);
            }
        } catch (Exception e) {
            rglog.error("系统异常!");
            return null;
        }
        return null;
    }
}
